package com.zhijiang.common.util;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author lichangyuan
 * @create 2021-10-08 10:50
 */
public class SpinLock {


    public static void main(String[] args) throws InterruptedException {

        //设置100容量线程池
        ExecutorService executorService = Executors.newFixedThreadPool(100);
        //计数器用于阻塞
        CountDownLatch countDownLatch = new CountDownLatch(10);
        //创建自旋锁对象
        SimpleSpinningLock simpleSpinningLock = new SimpleSpinningLock();



        for (int i = 0; i < 10; i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    simpleSpinningLock.lock();
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("子线程：" + Thread.currentThread().getName() + "执行");
                    simpleSpinningLock.unLock();
                    //确认已经连接完毕后再进行操作，将count值减1
                    countDownLatch.countDown();
                }
            });
        }
        //调用await()方法的线程会被挂起，它会等待直到count值为0才继续执行，没有则调用countDown则继续阻塞
        countDownLatch.await();
    }
}

class SimpleSpinningLock {

    /**
     * 持有锁的线程，null表示锁未被线程持有
     */
    private AtomicReference<Thread> sign = new AtomicReference<>();

    /**
     * 调用lock方法时，如果sign当前值为null，说明自旋锁还没有被占用，将sign设置为currentThread，并进行锁定。
     * 调用lock方法时，如果sign当前值不为null，说明自旋锁已经被其他线程占用，当前线程就会在while中继续循环检测。
     */
    public void lock() {
        //返回的正是执行当前代码指令的线程引用
        Thread currentThread = Thread.currentThread();
        //expect:它指定原子对象应为的值。
        //val:如果原子整数等于期望值，则该值指定要更新的值。
        while (!sign.compareAndSet(null, currentThread)) {
            //当ref为null的时候compareAndSet返回true，反之为false
            //通过循环不断的自旋判断锁是否被其他线程持有
        }
    }

    /**
     * 调用unlock方法时，会将sign置为空，相当于释放自旋锁。
     */
    public void unLock() {
        Thread currentThread = Thread.currentThread();
        //expect:它指定原子对象应为的值。
        //val:如果原子整数等于期望值，则该值指定要更新的值。
        sign.compareAndSet(currentThread, null);
    }
}
